﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;

namespace Xceed.Silverlight.Data
{
  internal sealed class ReactiveDataHelper : ContextualGroupParentDataHelper, IGroupDataHelper, IContextHelper
  {
    #region Constructor

    internal ReactiveDataHelper(
      IContextualGroupDataHelper childDataHelper,
      GroupHandler groupHandler,
      SortingHandler sortHandler,
      FilteringHandler filterHandler,
      IEnumerable<SortDescription> primaryKeys )
      : base( childDataHelper )
    {
      if( groupHandler == null )
        throw new ArgumentNullException( "groupHandler" );

      if( sortHandler == null )
        throw new ArgumentNullException( "sortHandler" );

      if( filterHandler == null )
        throw new ArgumentNullException( "filterHandler" );

      if( primaryKeys == null )
        throw new ArgumentNullException( "primaryKeys" );

      m_groupHandler = groupHandler;
      m_sortHandler = sortHandler;
      m_filterHandler = filterHandler;
      m_primaryKeys = primaryKeys;
    }

    #endregion

    public override DataPath ConvertBack( DataSourceContext context, DataPath value )
    {
      if( value == null )
        throw new ArgumentNullException( "value" );

      if( this.IsDummy( value ) )
        throw new DataInternalException();

      return base.ConvertBack( context, value );
    }

    public override object DeepConvertBack( DataSourceContext context, DataPath value )
    {
      if( value == null )
        throw new ArgumentNullException( "value" );

      if( !this.IsDummy( value ) )
        return base.DeepConvertBack( context, value );

      // We cannot convert the path any further.  The value cannot be converted into
      // a user object.
      return null;
    }

    public override object GetMemberValue( DataSourceContext context, string propertyPath, DataPath item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      if( this.IsDummy( item ) )
        throw new DataInternalException();

      return base.GetMemberValue( context, propertyPath, item );
    }

    public override bool TryGetMemberValue( DataSourceContext context, string propertyPath, DataPath item, out object value )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      if( this.IsDummy( item ) )
        throw new DataInternalException();

      return base.TryGetMemberValue( context, propertyPath, item, out value );
    }

    public override int Compare( DataSourceContext context, DataPath firstItem, DataPath secondItem )
    {
      if( object.ReferenceEquals( firstItem, secondItem ) )
        return 0;

      if( firstItem == null )
        throw new ArgumentNullException( "firstItem" );

      if( secondItem == null )
        throw new ArgumentNullException( "secondItem" );

      //If any of the items are in a Dummy position, we just return -1 as a default value.
      //This value is only used for index adjustment and does not need to be precise.
      if( this.IsDummy( firstItem ) || this.IsDummy( secondItem ) )
        return -1;

      return base.Compare( context, firstItem, secondItem );
    }

    public override int Compare( DataSourceContext context, DataPath item, SourcePosition position )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      if( position == null )
        throw new ArgumentNullException( "position" );

      if( position.IsBeforeFirst )
        return 1;

      if( position.IsAfterLast )
        return -1;

      ItemPosition itemPosition = position as ItemPosition;
      if( itemPosition != null )
        return this.Compare( context, item, ( DataPath )itemPosition.Item );

      //If any of the items are in a Dummy position, we just return -1 as a default value.
      //This value is only used for index adjustment and does not need to be precise.
      if( this.IsDummy( item ) )
        return -1;

      return base.Compare( context, item, position );
    }

    public override void Invalidate( DataSourceContext context, DataPath item )
    {
      if( ( item != null ) && ( !this.IsDummy( item ) ) )
      {
        base.Invalidate( context, item );
      }
    }

    public override bool IsGroupPath( DataSourceContext context, DataPath item )
    {
      if( this.IsDummy( item ) )
        throw new DataInternalException();

      return base.IsGroupPath( context, item );
    }

    public override DataPath GetGroupPath( DataSourceContext context, DataPath item )
    {
      if( this.IsDummy( item ) )
        throw new DataInternalException();

      return base.GetGroupPath( context, item );
    }

    public override SortPosition CreateSortPosition( DataSourceContext context, DataPath item, StartPosition startPosition )
    {
      if( this.IsDummy( item ) )
        throw new DataInternalException();

      return base.CreateSortPosition( context, item, startPosition );
    }

    internal bool IsDummy( DataPath item )
    {
      return ( item != null )
          && ( !item.LastChild.IsLoaded );
    }

    #region IContextHelper Members

    public DataSourceContext GetContext()
    {
      return new DataSourceContext(
        m_groupHandler,
        m_sortHandler,
        m_filterHandler,
        m_primaryKeys );
    }

    #endregion

    #region IItemHelper Members

    public object GetMemberValue( string propertyPath, DataPath item )
    {
      return this.GetMemberValue( this.GetContext(), propertyPath, item );
    }

    public bool TryGetMemberValue( string propertyPath, DataPath item, out object value )
    {
      return this.TryGetMemberValue( this.GetContext(), propertyPath, item, out value );
    }

    #endregion

    #region IConvertHelper Members

    public DataPath Convert( DataPath value )
    {
      return this.Convert( this.GetContext(), value );
    }

    public DataPath ConvertBack( DataPath value )
    {
      return this.ConvertBack( this.GetContext(), value );
    }

    public DataPath DeepConvert( object value )
    {
      return this.DeepConvert( this.GetContext(), value );
    }

    public object DeepConvertBack( DataPath value )
    {
      return this.DeepConvertBack( this.GetContext(), value );
    }

    #endregion

    #region ICompareHelper Members

    public int Compare( DataPath firstItem, DataPath secondItem )
    {
      return this.Compare( this.GetContext(), firstItem, secondItem );
    }

    public int Compare( DataPath item, SourcePosition position )
    {
      return this.Compare( this.GetContext(), item, position );
    }

    public int Compare( SourcePosition position, DataPath item )
    {
      return this.Compare( this.GetContext(), position, item );
    }

    public int Compare( SourcePosition firstPosition, SourcePosition secondPosition )
    {
      return this.Compare( this.GetContext(), firstPosition, secondPosition );
    }

    public int Compare( IList<SortDescription> sortDescriptions, DataPath firstItem, DataPath secondItem )
    {
      // Create new SortHandler based on sortDescriptions received
      SortingHandler newSortHandler = new SortingHandler();
      foreach( SortDescription sort in sortDescriptions )
      {
        newSortHandler.SortDescriptions.Add( sort );
      }

      // Create new DataSourceContext with existing values and new SortHandler
      DataSourceContext newContext = new DataSourceContext( m_groupHandler, newSortHandler, m_filterHandler, m_primaryKeys );
      return this.Compare( newContext, firstItem, secondItem );
    }

    public int Compare( string propertyPath, object firstValue, object secondValue )
    {
      return this.Compare( this.GetContext(), propertyPath, firstValue, secondValue );
    }

    #endregion

    #region IEditHelper Members

    public void Invalidate( DataPath item )
    {
      this.Invalidate( this.GetContext(), item );
    }

    #endregion

    #region IGroupHelper Members

    public bool IsGroupPath( DataPath item )
    {
      return this.IsGroupPath( this.GetContext(), item );
    }

    public DataPath GetGroupPath( DataPath item )
    {
      return this.GetGroupPath( this.GetContext(), item );
    }

    public SortPosition CreateSortPosition( DataPath item, StartPosition startPosition )
    {
      return this.CreateSortPosition( this.GetContext(), item, startPosition );
    }

    #endregion

    #region IDataHelper Members

    public IEnumerable<SortDescription> GetCurrentCompleteSortOrder()
    {
      return DataUtils.GetSortOrder(
        DataUtils.GetGroupNames( m_groupHandler.GroupDescriptions ),
        m_sortHandler.SortDescriptions,
        m_primaryKeys );
    }

    #endregion

    #region Private Fields

    private readonly GroupHandler m_groupHandler;
    private readonly SortingHandler m_sortHandler;
    private readonly FilteringHandler m_filterHandler;
    private readonly IEnumerable<SortDescription> m_primaryKeys;

    #endregion
  }
}
